package org.bjf.utils;

import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

import java.awt.*;
import java.io.*;
import java.util.List;
import java.util.*;

/**
 * 该类实现了基于模板的导出
 * 如果要导出序号，需要在excel中定义一个标识为sernums
 * 如果要替换信息，需要传入一个Map，这个map中存储着要替换信息的值，在excel中通过#来开头
 * 要从哪一行那一列开始替换需要定义一个标识为datas
 * 如果要设定相应的样式，可以在该行使用styles完成设定，此时所有此行都使用该样式
 * 如果使用defaultStyls作为表示，表示默认样式，如果没有defaultStyles使用datas行作为默认样式
 */
public class BgExcelWriter {
    /**
     * 数据行标识
     */
    private final static String DATA_LINE = "datas";
    /**
     * 默认样式标识
     */
    private final static String DEFAULT_STYLE = "defaultStyles";
    /**
     * 行样式标识
     */
    private final static String STYLE = "styles";
    /**
     * 插入序号样式标识
     */
    private final static String SER_NUM = "sernums";
    private static BgExcelWriter writer;
    private Workbook wb;
    private Sheet sheet;
    /**
     * 数据的初始化列数
     */
    private int initColIndex;
    /**
     * 数据的初始化行数
     */
    private int initRowIndex;
    /**
     * 当前列数
     */
    private int curColIndex;
    /**
     * 当前行数
     */
    private int curRowIndex;
    /**
     * 当前行对象
     */
    private Row curRow;
    /**
     * 默认样式
     */
    private CellStyle defaultStyle;
    /**
     * 默认行高
     */
    private float rowHeight = 13.5F;
    /**
     * 存储某一列所对应的样式
     */
    private Map<Integer, CellStyle> styles;
    /**
     * 序号的列
     */
    private int serColIndex;

    private BgExcelWriter() {

    }

    public static BgExcelWriter getInstance() {
        writer = new BgExcelWriter();
        return writer;
    }

    /**
     * 创建空白工作本
     */
    public BgExcelWriter newBlankBook() {
        wb = new XSSFWorkbook();
//        wb.createSheet();
//        initTemplate();
        return this;
    }

    /**
     * 从classpath路径下读取相应的模板文件
     */
    public BgExcelWriter readTemplateByClasspath(String path) {

        try {
            wb = WorkbookFactory.create(BgExcelWriter.class.getResourceAsStream(path));
            this.sheet = wb.getSheetAt(0);
        } catch (IOException e) {
            throw new RuntimeException("读取模板不存在！请检查", e);
        }

        initTemplate();
        return this;
    }

    /**
     * 从某个路径来读取模板
     *
     * @param path 磁盘路径
     */
    public BgExcelWriter readTemplateByPath(String path) {
        try {
            wb = WorkbookFactory.create(new File(path));
            this.sheet = wb.getSheetAt(0);
        } catch (IOException e) {
            throw new RuntimeException("读取模板不存在！请检查", e);
        }
        initTemplate();
        return this;
    }

    /**
     * 将文件写到相应的路径下
     */
    public void writeToFile(String filepath) {
        try (FileOutputStream fos = new FileOutputStream(filepath)) {
            wb.write(fos);
        } catch (IOException e) {
            throw new RuntimeException("写入数据失败:" + e.getMessage(), e);
        }
    }

    /**
     * 将文件写到某个输出流中
     */
    public void writeToStream(OutputStream os) {
        try {
            wb.write(os);
        } catch (IOException e) {
            throw new RuntimeException("写入流失败:" + e.getMessage(), e);
        }
    }

    /**
     * 将文件写到某个输出流中
     */
    public InputStream getInputStream() {
        try {
            ByteArrayOutputStream os = new ByteArrayOutputStream();
            wb.write(os);
            return new ByteArrayInputStream(os.toByteArray());
        } catch (IOException e) {
            throw new RuntimeException("写入流失败:" + e.getMessage(), e);
        }
    }


    /**
     * 创建相应的元素，基于String类型
     */
    public Cell createCell(Object value) {
        Cell c = curRow.createCell(curColIndex);
        setCellStyle(c);
        setCellValue(c, value);
        curColIndex++;
        return c;
    }

    public CellStyle createCellStyle() {
        return wb.createCellStyle();
    }

    /**
     * 设置某个元素的样式
     */
    private void setCellStyle(Cell c) {
        setCellStyle(c, curColIndex);
    }

    /**
     * 设置某个元素的样式
     */
    private void setCellStyle(Cell c, int colIndex) {
        if (styles != null && styles.containsKey(colIndex)) {
            c.setCellStyle(styles.get(colIndex));
        } else {
            c.setCellStyle(defaultStyle);
        }
    }

    /**
     * 返回该名字sheet索引
     */
    public int getSheetIndex(String sheetName) {
        return wb.getSheetIndex(sheetName);
    }

    /**
     * 创建新行
     */
    public void createRow() {
        curRow = sheet.createRow(curRowIndex);
        curRow.setHeightInPoints(rowHeight);
        curRowIndex++;
        curColIndex = initColIndex;
    }

    /**
     * 创建新行，在使用时只要添加完一行，需要调用该方法创建
     */
    public void createRow(Object... cellValues) {
        createRow(Arrays.asList(cellValues));
    }

    /**
     * 创建新行，在使用时只要添加完一行，需要调用该方法创建
     */
    public void createRow(List<Object> cellValues) {
        createRow();

        for (Object value : cellValues) {
            Cell c = curRow.createCell(curColIndex);
            setCellStyle(c);
            setCellValue(c, value);
            curColIndex++;
        }
    }

    private void setCellValue(Cell c, Object value) {
        if (value == null) {
            c.setCellValue("");
        } else if (value instanceof String) {
            c.setCellValue(value.toString());
        } else if (value instanceof Calendar) {
            c.setCellValue((Calendar) value);
        } else if (value instanceof Boolean) {
            c.setCellValue((Boolean) value);
        } else if (value instanceof RichTextString) {
            c.setCellValue((RichTextString) value);
        } else if (value instanceof Number) {
            c.setCellValue(((Number) value).doubleValue());
        } else {
            c.setCellValue(value.toString());
        }
    }

    public void setStarPosition(int rowIndex, int colIndex) {
        initColIndex = colIndex;
        initRowIndex = rowIndex;
        curColIndex = initColIndex;
        curRowIndex = initRowIndex;
    }

    /**
     * 插入序号，会自动找相应的序号标示的位置完成插入
     */
    public void insertSer() {
        int index = 1;
        Row row = null;
        Cell c = null;
        for (int i = initRowIndex; i < curRowIndex; i++) {
            row = sheet.getRow(i);
            c = row.createCell(serColIndex);
            setCellStyle(c, serColIndex);
            c.setCellValue(index++);
        }
    }

    /**
     * 根据map替换相应的常量，通过Map中的值来替换#开头的值
     */
    public void replaceFinalData(Map<String, String> datas) {
        if (datas == null) {
            return;
        }
        for (Row row : sheet) {
            for (Cell c : row) {
                if (c.getCellType() != CellType.STRING) {
                    continue;
                }
                String str = c.getStringCellValue().trim();
                if (str.startsWith("#")) {
                    if (datas.containsKey(str.substring(1))) {
                        c.setCellValue(datas.get(str.substring(1)));
                    }
                }
            }
        }
    }

    private void initTemplate() {
        initConfigData();
    }

    /**
     * 初始化数据信息
     */
    private void initConfigData() {
        boolean findData = false;
        boolean findSer = false;
        for (Row row : sheet) {
            if (findData) {
                break;
            }
            for (Cell c : row) {
                if (c.getCellType() != CellType.STRING) {
                    continue;
                }
                String str = c.getStringCellValue().trim();
                if (str.equals(SER_NUM)) {
                    serColIndex = c.getColumnIndex();
                    findSer = true;
                }
                if (str.equals(DATA_LINE)) {
                    initColIndex = c.getColumnIndex();
                    initRowIndex = row.getRowNum();
                    curColIndex = initColIndex;
                    curRowIndex = initRowIndex;
                    findData = true;
                    defaultStyle = c.getCellStyle();
                    rowHeight = row.getHeightInPoints();
                    initStyles();
                    break;
                }
            }
        }
        if (!findData) {
            initStyles();
        }
        if (!findSer) {
            initSer();
        }
    }

    /**
     * 初始化序号位置
     */
    private void initSer() {
        for (Row row : sheet) {
            for (Cell c : row) {
                if (c.getCellType() != CellType.STRING) {
                    continue;
                }
                String str = c.getStringCellValue().trim();
                if (str.equals(SER_NUM)) {
                    serColIndex = c.getColumnIndex();
                }
            }
        }
    }

    /**
     * 初始化样式信息
     */
    private void initStyles() {
        styles = new HashMap<>(16);
        for (Row row : sheet) {
            for (Cell c : row) {
                if (c.getCellType() != CellType.STRING) {
                    continue;
                }
                String str = c.getStringCellValue().trim();
                if (str.equals(DEFAULT_STYLE)) {
                    defaultStyle = c.getCellStyle();
                }
                if (str.equals(STYLE)) {
                    styles.put(c.getColumnIndex(), c.getCellStyle());
                }
                if (str.equals(SER_NUM)) {
                    styles.put(c.getColumnIndex(), c.getCellStyle());
                }
            }
        }
    }

    /**
     * 设置区域样式
     *
     * @param p1        起始坐标
     * @param p2        结束坐标
     * @param cellStyle 样式
     */
    public void setRegionStyle(Point p1, Point p2, CellStyle cellStyle) {
        if (p1.x > p2.x || p1.y > p2.y) {
            throw new RuntimeException("起始坐标大于结束坐标");
        }

        int lines = p2.x - p1.x;
        int cols = p2.y - p1.y;
        for (int i = 0; i < lines; i++) {
            int rowIndex = p1.x + i;
            Row row = sheet.getRow(rowIndex);
            if (row == null) {
                row = sheet.createRow(rowIndex);
            }
            for (int j = 0; j < cols; j++) {
                int colIndex = p1.y + j;
                Cell cell = row.getCell(colIndex);
                if (cell == null) {
                    cell = row.createCell(colIndex);
                }
                cell.setCellStyle(cellStyle);
            }
        }
    }

    public void createSheet(String sheetName) {
        this.sheet = wb.createSheet(sheetName);
        initColIndex = 0;
        initRowIndex = 0;
        curColIndex = 0;
        curRowIndex = 0;
    }

    public void cloneSheetByTemplate(String sheetName) {
        // 第0个sheet为模板
        this.sheet = wb.cloneSheet(0);
        wb.setSheetName(wb.getSheetIndex(this.sheet), sheetName);

        initTemplate();
    }

    public void deleteTemplateSheet() {
        // 删除第一个模板sheet
        wb.removeSheetAt(0);
    }

    /**
     * 此行合并多少个列单元格
     *
     * @param count   合并多少个列单元格
     * @param content 合并后填充的内容
     */
    public void mergeCol(int count, String content) {
        //合并单元格
        CellRangeAddress cellRangeAddress = new CellRangeAddress(curRow.getRowNum(), curRow.getRowNum(), curColIndex, curColIndex + count - 1);
        this.sheet.addMergedRegion(cellRangeAddress);

        Cell cell = curRow.createCell(curColIndex);
        //居中
        CellStyle style = createCellStyle();
        style.setAlignment(HorizontalAlignment.CENTER);
        cell.setCellStyle(style);

        //设置内容
        setCellValue(cell, content);
    }
}
